import {NotFoundError, t} from 'elysia'
import {ElysiaApp} from '../index'
import crypto from 'crypto'
import {ForbiddenError, UnauthorizedError, encrypt, isAdmin, isSelf, removeToken} from '../utils/auth'
import {defaultPwd} from '../utils/const'

const addSchema = t.Object({
  name: t.String({maxLength: 20}),
  username: t.String({maxLength: 20}),
  email: t.Optional(t.String({format: 'email'})),
  phone: t.Optional(
    t.String({
      pattern:
        '((\\d{11})|^((\\d{7,8})|(\\d{4}|\\d{3})-(\\d{7,8})|(\\d{4}|\\d{3})-(\\d{7,8})-(\\d{4}|\\d{3}|\\d{2}|\\d{1})|(\\d{7,8})-(\\d{4}|\\d{3}|\\d{2}|\\d{1}))$)',
    }),
  ),
  nickName: t.Optional(t.String()),
  remark: t.Optional(t.String()),
  avatar: t.Optional(t.String()),
})

const updSchema = t.Intersect([addSchema, t.Object({id: t.Number()})])

export default (app: ElysiaApp) =>
  app
    // 获取用户分页列表
    .get(
      '/page/list',
      async ({prisma, query}) => {
        const {page, pageSize = 10, name, phone} = query
        const _res = await prisma.$transaction([
          prisma.user.findMany({
            skip: (Number(page) - 1) * Number(pageSize),
            take: Number(pageSize),
            where: {
              AND: {
                name: {contains: name},
                phone: {contains: phone},
                username: {not: 'admin'},
              },
            },
            select: {
              id: true,
              name: true,
              username: true,
              avatar: true,
              email: true,
              phone: true,
              nickName: true,
              remark: true,
              updateTime: true,
            },
          }),
          prisma.user.count({
            where: {
              AND: {name: {contains: name}, phone: {contains: phone}, username: {not: 'admin'}},
            },
          }),
        ])
        return {list: _res[0], total: _res[1]}
      },
      {
        query: t.Object({
          page: t.Numeric(),
          pageSize: t.Optional(t.Numeric()),
          name: t.Optional(t.String()),
          phone: t.Optional(t.String()),
        }),
      },
    )
    // 获取用户下拉列表
    .get(
      '/list',
      async ({query, prisma}) => {
        const {name, phone} = query
        return await prisma.user.findMany({
          select: {
            id: true,
            name: true,
            username: true,
            avatar: true,
            email: true,
            phone: true,
            nickName: true,
          },
          where: {
            AND: {
              name: {contains: name},
              phone: {contains: phone},
            },
            NOT: {username: 'admin'},
          },
        })
      },
      {
        query: t.Object({
          name: t.Optional(t.String()),
          phone: t.Optional(t.String()),
        }),
      },
    )
    // 获取单个用户信息
    .get(
      '/info',
      ({query: {id}, prisma}) => {
        return prisma.user.findUnique({
          where: {id},
          select: {
            id: true,
            name: true,
            username: true,
            avatar: true,
            email: true,
            phone: true,
            nickName: true,
            remark: true,
          },
        })
      },
      {
        query: t.Object({
          id: t.Numeric(),
        }),
      },
    )
    // 新增用户
    .post(
      '/add',
      async ({body, prisma}) => {
        const {name, username, email, phone, nickName, remark} = body
        const _user = await prisma.user.findFirst({where: {OR: [{username}, {phone}]}})
        if (_user !== null) throw new ForbiddenError('此用户已存在，请勿重新添加')
        const pSalt = crypto.randomBytes(16).toString('hex')
        const iSalt = crypto.randomBytes(16).toString('hex')
        await prisma.user.create({
          data: {
            name,
            username,
            password: encrypt(defaultPwd, pSalt, iSalt),
            avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
            email,
            phone,
            nickName,
            remark,
            pSalt,
            iSalt,
          },
        })
        return '用户新增成功'
      },
      {body: addSchema},
    )
    // 修改用户
    .patch(
      '/upd',
      async ({body, prisma}) => {
        const {id, name, username, email, phone, nickName, remark, avatar} = body
        let _user = await prisma.user.findFirst({where: {OR: [{username}, {phone}]}})
        if (_user && _user.id !== id) throw new ForbiddenError('此用户名或手机号已存在，请重试')
        _user = await prisma.user.findUnique({where: {id}})
        if (!_user) throw new NotFoundError('此用户不存在')
        if (_user.username === 'admin' && _user.username !== username) throw new ForbiddenError('管理员用户名不可更改')
        await prisma.user.update({
          where: {id},
          data: {name, username, email, phone, nickName, remark, avatar},
        })
        return '用户修改成功'
      },
      {body: updSchema},
    )
    .patch(
      '/upd-pwd',
      async ({body, prisma, redis, store: {curUser}}) => {
        const {id, oldPwd, newPwd} = body
        if (oldPwd === newPwd) throw new ForbiddenError('原密码与新密码相同，请重试')
        const user = await prisma.user.findUnique({where: {id}})
        if (!user) throw new NotFoundError('此用户不存在')
        const {password, pSalt, iSalt} = user
        if (encrypt(oldPwd, pSalt, iSalt) === password) {
          // 原密码与用户密码一致
          const _pSalt = crypto.randomBytes(16).toString('hex')
          const _iSalt = crypto.randomBytes(16).toString('hex')
          await prisma.user.update({
            where: {id},
            data: {password: encrypt(newPwd, _pSalt, _iSalt), pSalt: _pSalt, iSalt: _iSalt},
          })
          await removeToken(prisma, redis, id)
          if (isSelf(id, curUser)) {
            throw new UnauthorizedError('当前用户密码已更改，请重新登录')
          }
        } else {
          throw new ForbiddenError('用户原密码输入有误')
        }
        return '密码更改成功'
      },
      {
        body: t.Object({
          id: t.Number(),
          oldPwd: t.String(),
          newPwd: t.String(),
        }),
      },
    )
    // 删除用户
    .delete(
      '/del/:id',
      async ({params: {id}, prisma, store: {curUser}}) => {
        if (!(await isAdmin(prisma, curUser))) throw new ForbiddenError('普通用户不可删除，请联系管理员操作')
        if (isSelf(id, curUser)) throw new ForbiddenError('不可删除当前登录用户')
        const delUserRole = prisma.userRole.deleteMany({where: {userId: id}})
        const delUser = prisma.user.delete({where: {id}})
        await prisma.$transaction([delUserRole, delUser])
      },
      {
        params: t.Object({
          id: t.Numeric(),
        }),
      },
    )
